/******************************************************************************\
    HttpResponse.cpp discribes classes of various http responses.
    Copyright (C) 2008  Victor Vinogradov (fly3333@gmail.com)

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
\******************************************************************************/

#include ".\HttpResponse.h"
#include <Common\Memory.hpp>
#include <Common\String.h>
#include <Common\Time.h>
using namespace std;

std::string cWardHttpResponse::TimeStr( unsigned long long time )
{
	static const char *day_of_week_name[] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
	static const char *month_name[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
	unsigned short year, month, day, day_of_week, hour, minute, second;
	SplitTime( time, &year, &month, &day, &day_of_week, &hour, &minute, &second, NULL );
	return StringPrintf( "%s, %02d %s %04d %02d:%02d:%02d GMT", day_of_week_name[ day_of_week - 1 ], day, month_name[ month - 1 ], year, hour, minute, second );
}

cDataResponse::cDataResponse( const std::string &data )
	: ContentData( data )
	, ContentOffset( 0 )
	, Time( GetTime() )
{
}

int cDataResponse::Get()
{
	if( ContentOffset == ContentData.size() )
		return -1;
	return ( unsigned char )ContentData[ ContentOffset++ ];
}

unsigned long long cDataResponse::GetLength() const
{
	return ContentData.size();
}

std::map< std::string, std::string > cDataResponse::GetContentInfo() const
{
	map< string, string > result;
	result.insert( make_pair( "date", TimeStr( Time ) ) );
	result.insert( make_pair( "content-type", "text/html" ) );
	result.insert( make_pair( "content-length", StringPrintf( "%d", ( int )ContentData.length() ) ) );
	return result;
}

cOkResponse::cOkResponse( const std::string &data, const std::string &mime_type )
	: cDataResponse( data )
	, MimeType( mime_type )
{
}

int cOkResponse::GetStatusCode() const
{
	return 200;
}

const char *cOkResponse::GetComment() const
{
	return "OK";
}

std::map< std::string, std::string > cOkResponse::GetContentInfo() const
{
	map< string, string > result = cDataResponse::GetContentInfo();
	result[ "content-type" ] = MimeType;
	return result;
}

cNotFoundResponse::cNotFoundResponse( const std::string &data )
	: cDataResponse( data )
{
}

int cNotFoundResponse::GetStatusCode() const
{
	return 404;
}

const char *cNotFoundResponse::GetComment() const
{
	return "Not Found";
}

cForbiddenResponse::cForbiddenResponse( const std::string &data )
	: cDataResponse( data )
{
}

int cForbiddenResponse::GetStatusCode() const
{
	return 403;
}

const char *cForbiddenResponse::GetComment() const
{
	return "Forbidden";
}

int cEmptyResponse::Get()
{
	return -1;
}

unsigned long long cEmptyResponse::GetLength() const
{
	return 0;
}

cMovedPermanentlyResponse::cMovedPermanentlyResponse( const std::string &location )
	: Location( location )
{
}

int cMovedPermanentlyResponse::GetStatusCode() const
{
	return 301;
}

const char *cMovedPermanentlyResponse::GetComment() const
{
	return "Moved Permanently";
}

std::map< std::string, std::string > cMovedPermanentlyResponse::GetContentInfo() const
{
	map< string, string > result;
	result.insert( make_pair( "location", Location ) );
	result.insert( make_pair( "content-length", "0" ) );
	return result;
}

cFoundResponse::cFoundResponse( const std::string &location )
	: Location( location )
{
}

int cFoundResponse::GetStatusCode() const
{
	return 302;
}

const char *cFoundResponse::GetComment() const
{
	return "Found";
}

std::map< std::string, std::string > cFoundResponse::GetContentInfo() const
{
	map< string, string > result;
	result.insert( make_pair( "location", Location ) );
	result.insert( make_pair( "content-length", "0" ) );
	return result;
}

cSetCookieResponse::cSetCookieResponse( const std::string &location, const std::string &cookie )
	: cFoundResponse( location )
	, Cookie( cookie )
{
}

std::map< std::string, std::string > cSetCookieResponse::GetContentInfo() const
{
	map< string, string > result = cFoundResponse::GetContentInfo();
	result.insert( make_pair( "set-cookie", Cookie ) );
	return result;
}

cBaseFileResponse::cBaseFileResponse( const cFile &file, unsigned long long offset_first, unsigned long long offset_last, unsigned long max_buffer_size )
	: File( file )
	, FirstOffset( offset_first )
	, CurrentOffset( offset_first )
	, LastOffset( offset_last )
	, Buffer( max_buffer_size )
	, BufferOffset( 0 )
	, BufferSize( 0 )
	, MaxBufferSize( max_buffer_size )
{
}

int cBaseFileResponse::Get()
{
	if( CurrentOffset == LastOffset )
		return -1;
	if( BufferOffset == BufferSize )
	{
		BufferOffset = 0;
		if( !File.Read( Buffer, MaxBufferSize, &BufferSize, NULL ) )
			return -1;
	}
	++CurrentOffset;
	return ( unsigned char )Buffer[ BufferOffset++ ];
}

unsigned long long cBaseFileResponse::GetLength() const
{
	return LastOffset - FirstOffset;
}

std::map< std::string, std::string > cBaseFileResponse::GetContentInfo() const
{
	map< string, string > result;
	result.insert( make_pair( "content-type", "application/octet-stream" ) );
	result.insert( make_pair( "content-length", StringPrintf( "%I64u", LastOffset - FirstOffset ) ) );
	FILETIME file_time;
	File.GetTimes( NULL, NULL, &file_time ); 
	ULARGE_INTEGER time;
	time.HighPart = file_time.dwHighDateTime;
	time.LowPart = file_time.dwLowDateTime;
	result.insert( make_pair( "date", TimeStr( time.QuadPart ) ) );
	return result;
}

cFileResponse::cFileResponse( const cFile &file, unsigned long max_buffer_size )
	: cBaseFileResponse( file, 0, file.Size(), max_buffer_size )
{
}

int cFileResponse::GetStatusCode() const
{
	return 200;
}

const char *cFileResponse::GetComment() const
{
	return "OK";
}

cPartialFileResponse::cPartialFileResponse( const cFile &file, unsigned long long offset_first, unsigned long long offset_last, unsigned long max_buffer_size )
	: cBaseFileResponse( file, offset_first, offset_last, max_buffer_size )
{
}

int cPartialFileResponse::GetStatusCode() const
{
	return 206;
}

const char *cPartialFileResponse::GetComment() const
{
	return "Partial Content";
}

std::map< std::string, std::string > cPartialFileResponse::GetContentInfo() const
{
	map< string, string > result = cBaseFileResponse::GetContentInfo();
	result.insert( make_pair( "accept-ranges", "bytes" ) );
	result.insert( make_pair( "content-range", StringPrintf( "bytes %I64u-%I64u/%I64u", FirstOffset, LastOffset - 1, File.Size() ) ) );
	return result;
}
